// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package syscall

import "unsafe"
import "errors"
import "runtime"

/*

func Getgroups() (gids []int, err error) {
	return nil, ENOSYS
}

func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }

func NsecToTimespec(nsec int64) (ts Timespec) {
	ts.Sec = Timespec_sec_t(nsec / 1e9)
	ts.Nsec = Timespec_nsec_t(nsec % 1e9)
	return
}

func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3 }

func NsecToTimeval(nsec int64) (tv Timeval) {
	nsec += 999 // round up to microsecond
	tv.Sec = Timeval_sec_t(nsec / 1e9)
	tv.Usec = Timeval_usec_t(nsec % 1e9 / 1e3)
	return
}
*/

func cstrToString(str *byte) string {
	b := (*[1024]byte)(unsafe.Pointer(str))
	i := 0
	for b[i] != 0 {
		i++
	}
	return string(b[:i])
}

// Defined in C.
func stringData(str string) *byte

//extern malloc
func libc_malloc(sz _size_t) *byte

//extern memcpy
func memcpy(dest *byte, src *byte, sz _size_t)

func stringMallocData(str string) *byte {
	n := libc_malloc(_size_t(len(str)))
	memcpy(n, stringData(str), _size_t(len(str)))
	return n
}

type SysProcAttr struct {
}

//extern str_error
func str_error(errnum int) *byte

func StrError(errnum int) string {
	bp := str_error(errnum)
	return cstrToString(bp)
}

func (e Errno) String() string {
	return StrError(int(e))
}

//extern task_kill
func task_kill(task_id _task_id_t) int

func TaskKill(id int) error {
	rc := task_kill(_task_id_t(id))

	if rc == 0 {
		return nil
	} else {
		return Errno(rc)
	}
}

//extern task_get_id
func task_get_id() _task_id_t

func TaskGetID() int {
	rc := int(task_get_id())
	return rc
}

//extern open
func open(path *byte, oflag int) int

func Open(path string, oflag int) (fd int, err error) {
	rc := open(stringData(path), oflag)

	if rc >= 0 {
		return rc, nil
	} else {
		return -1, Errno(rc)
	}
}

//extern close
func close(fildes int) int

func Close(fildes int) error {
	rc := close(fildes)

	if rc == 0 {
		return nil
	} else {
		return Errno(rc)
	}
}

//extern read
func read(fildes int, buf *byte, nbyte _size_t) _ssize_t

func Read(fildes int, buf []byte) (n int, err error) {
	if len(buf) == 0 {
		return 0, nil
	}

	retval := int(read(fildes, (*byte)(unsafe.Pointer(&buf[0])), _size_t(len(buf))))

	if retval < 0 {
		return 0, Errno(retval)
	}
	return retval, nil
}

//extern write
func write(fildes int, buf *byte, nbyte _size_t) _ssize_t

func Write(fildes int, buf []byte) (n int, err error) {
	if len(buf) == 0 {
		return 0, nil
	}

	retval := int(write(fildes, (*byte)(unsafe.Pointer(&buf[0])), _size_t(len(buf))))
	if retval < 0 {
		return 0, Errno(retval)
	}
	return retval, nil
}

//extern read_all
func read_all(fildes int, buf *byte, nbyte _size_t) _ssize_t

func SysReadAll(fildes int, buf []byte) (n int, err error) {
	if len(buf) == 0 {
		return 0, nil
	}

	retval := int(read_all(fildes, (*byte)(unsafe.Pointer(&buf[0])), _size_t(len(buf))))

	if retval < 0 {
		return 0, Errno(retval)
	}
	return retval, nil
}

func ReadAll(fildes int, buf []byte) (n int, err error) {
	if len(buf) == 0 {
		return 0, nil
	}

	total_read := 0
	for {
		cnt, e := Read(fildes, buf)

		total_read += cnt
		buf = buf[cnt:]

		if cnt == 0 || e != nil || len(buf) == 0 {
			return total_read, e
		}
	}
}

//extern write_all
func write_all(fildes int, buf *byte, nbyte _size_t) _ssize_t

func SysWriteAll(fildes int, buf []byte) (n int, err error) {
	if len(buf) == 0 {
		return 0, nil
	}

	retval := int(write_all(fildes, (*byte)(unsafe.Pointer(&buf[0])), _size_t(len(buf))))

	if retval < 0 {
		return 0, Errno(retval)
	}
	return retval, nil
}

func WriteAll(fildes int, buf []byte) (n int, err error) {
	if len(buf) == 0 {
		return 0, nil
	}

	total_written := 0
	for {
		cnt, e := Write(fildes, buf)

		total_written += cnt
		buf = buf[cnt:]

		if e != nil || len(buf) == 0 {
			return total_written, e
		}
	}
}

//extern lseek
func lseek(fildes int, offset _off64_t, whence int) _off64_t

func LSeek(fildes int, offset int64, whence int) (off int64, err error) {
	retval := lseek(fildes, _off64_t(offset), whence)

	if retval < 0 {
		return -1, errors.New("syscall.LSeek failed")
	} else {
		return int64(retval), nil
	}
}

func Getpagesize() int {
	return _PAGE_SIZE
}

//extern chdir
func chdir(path *byte) int

func Chdir(path string) error {
	rc := chdir(stringData(path))

	if rc == 0 {
		return nil
	} else {
		return Errno(rc)
	}
}

//extern getcwd
func getcwd(buf *byte, size _size_t) *byte

func Getcwd() string {
	var buffer [1024]byte

	getcwd((*byte)(unsafe.Pointer(&buffer[0])), _size_t(len(buffer)))

	len := 0
	for buffer[len] != 0 {
		len++
	}
	return string(buffer[:len])
}

//extern task_spawnvf
func task_spawnvf(id *_task_id_t, path *byte, args **byte, files **int) int

func TaskSpawn(path string, args []string, files []*int) (id int, err error) {
	cargs := make([]*byte, len(args))
	for i, str := range args {
		cargs[i] = stringData(str)
	}

	var tid _task_id_t

	rc := task_spawnvf(&tid, stringData(path), (**byte)(unsafe.Pointer(&cargs[0])), (**int)(unsafe.Pointer(&files[0])))

	if rc < 0 {
		return 0, Errno(rc)
	} else {
		return int(tid), nil
	}
}

//extern mkdir
func mkdir(path *byte, mode _mode_t) int

func Mkdir(path string, mode ModeT) error {
	rc := mkdir(stringData(path), _mode_t(mode))

	if rc == 0 {
		return nil
	} else {
		return Errno(rc)
	}
}

//extern stat
func stat(path *byte, st *StatT) int

func Stat(path string, st *StatT) error {
	rc := stat(stringData(path), st)

	if rc == 0 {
		return nil
	} else {
		return Errno(rc)
	}
}

//extern fstat
func fstat(fd int, st *StatT) int

func FStat(fd int, st *StatT) error {
	rc := fstat(fd, st)
	if rc == 0 {
		return nil
	} else {
		return Errno(rc)
	}
}

type DIR *_DIR

//extern opendir
func opendir(dirname *byte) DIR

func OpenDir(dirname string) (d *DIR, err error) {
	dir := opendir(stringData(dirname))

	if dir == nil {
		return nil, errors.New("opendir failed")
	}

	dirp := &dir
	runtime.SetFinalizer(dirp, func() { closedir(*dirp) })

	return dirp, nil
}

//extern readdir
func readdir(dirp DIR) *_dirent

// Returns empty string in place of NULL.
func ReadDir(dir *DIR) string {
	if dir == nil || *dir == nil {
		return ""
	}

	entry := readdir(*dir)

	if entry == nil {
		return ""
	}
	return cstrToString((*byte)(unsafe.Pointer(&entry.d_name[0])))
}

//extern rewinddir
func rewinddir(dir DIR)

func RewindDir(dir *DIR) {
	if dir != nil && *dir != nil {
		rewinddir(*dir)
	}
}

//extern closedir
func closedir(dir DIR) int

func CloseDir(dir *DIR) error {
	if dir == nil || *dir == nil {
		return EINVAL
	}

	rc := closedir(*dir)

	// No need for finalizer any more.
	runtime.SetFinalizer(dir, nil)

	// Prevent using after close.
	*dir = nil

	if rc == 0 {
		return nil
	} else {
		return Errno(rc)
	}
}

//extern unlink
func unlink(path *byte) int

func Unlink(path string) error {
	rc := unlink(stringData(path))

	if rc == 0 {
		return nil
	} else {
		return Errno(rc)
	}
}

//extern rmdir
func rmdir(path *byte) int

func Rmdir(path string) error {
	rc := rmdir(stringData(path))
	if rc == 0 {
		return nil
	} else {
		return Errno(rc)
	}
}

//extern exit
func exit(code int)

func Exit(code int) {
	exit(code)

	// Not reached.
}

//extern rename
func rename(old *byte, new *byte) int

func Rename(old string, new string) error {
	rc := rename(stringData(old), stringData(new))
	if rc != 0 {
		return Errno(rc)
	}
	return nil
}

//extern ftruncate
func ftruncate(fildes int, length _aoff64_t) int

func FTruncate(fd int, length int64) error {
	rc := ftruncate(fd, _aoff64_t(length))
	if rc != 0 {
		return Errno(rc)
	}
	return nil
}

//extern task_wait
func task_wait(id _task_id_t, texit *_task_exit_t, retval *int) int

func TaskWait(id int) (texit int, retval int, err error) {
	var exit _task_exit_t

	rc := task_wait(_task_id_t(id), &exit, &retval)

	if rc != 0 {
		return 0, 0, Errno(rc)
	}
	return int(exit), retval, nil
}

//extern fsync
func fsync(fd int) int

func FSync(fd int) error {
	rc := fsync(fd)
	if rc != 0 {
		return Errno(rc)
	}
	return nil
}
